

#include <string.h>
#include "User.h"
#include "RingChunk.h"
#include "BlockDeviceConfig.h"
#include "BlockDevice.h"

#include "Bit.h"
#include "Utility.h"
#include "SoftCrc.h"

#include "Debug.h"

#define USER_DBG	1

#define USER_MAX	5000

RingChunk_T userChunk;

/*---------------------------------------------------------*/

#if 0
int User_DoFind(Uid_T uid, User_T* user, uint32_t* index){
	if(userChunk.size == 0){
		return -1;
	}
	if(RingChunk_FindItemNext(&userChunk, uid, user, index)){
		return -2;
	}
	return 0;
}

/*
查找用户

@uid: 要查找的用户的ID
@user: 指向返回用户记录信息的指针，若为NULL，不返回用户信息。
@index: 存储用户信息的索引的指针
return：找到用户，返回其0；未找到用户，返回负值。
*/
int User_Find(Uid_T uid, User_T* user, uint32_t* index){
	BD_Lock();
	int ret = User_DoFind(uid, user, index);
	BD_Unlock();
	return ret;
}
/*-----------------------------------*/

int User_DoSet(User_T* user){
	if(user->uid == 0){
	//非法用户信息
		return -2;
	}
	uint32_t index;
	if(User_DoFind(user->uid, 0, &index)){
		//用户不存在
		return -1;
	}
	//将用户信息写入新的位置
	if(RingChunk_Append(&userChunk, user)){
		return -3;
	}
	//随后会删除旧用户信息，用户数量并没有增加
	userChunk.size--;
	//更新游标
	RingChunk_UpdateInfo(&userChunk);
	//删除旧用户信息
	return RingChunk_Write(&userChunk, index, &(Uid_T){0u}, sizeof(Uid_T));
}


/*
修改用户信息

@user: 新的用户信息的指针

return: 成功修改返回0，否则返回负数

*/
int User_Set(User_T* user){
	BD_Lock();
	int ret = User_DoSet(user);
	BD_Unlock();
	return ret;
}

/*-----------------------------------------*/
static int User_DoAdd(User_T* user){
	if(user->uid == 0){
		//非法用户信息
		return -2;
	}
	if(userChunk.size >= userChunk.maxSize){
		//用户已满
		return -3;
	}
	if(!User_DoFind(user->uid, 0, 0)){
		//用户已存在
		return -1;
	}	
	//将用户信息写入新的位置
	if(RingChunk_Append(&userChunk, user)){
		return -4;
	}
	//更新游标
	RingChunk_UpdateInfo(&userChunk);
	return 0;
}



/*
添加用户

@user：添加的用户信息的指针

return: 成功修改返回0，否则返回负数

*/

int User_Add(User_T* user){
	BD_Lock();
	int ret = User_DoAdd(user);
	BD_Unlock();
	return ret;
}



static int User_DoRemove(Uid_T uid){	
	RingChunk_Remove(&userChunk, uid);
	RingChunk_UpdateInfo(&userChunk);
	return 0;
}


/*
删除用户

@uid：要删除的用户的ID

return: 成功删除返回0，否则返回负数

*/
int User_Remove(Uid_T uid){	
	BD_Lock();
	int ret = User_DoRemove(uid);
	BD_Unlock();
	return ret;
}



int User_Read(User_T * user){
	int ret;

	BD_Lock();
	ret = RingChunk_FindNext(&userChunk, 0, 0);
	if(ret == 0){
		ret = RingChunk_CachReadItem(&userChunk, user);
	}
	BD_Unlock();
	if(ret){
		return ret;
	}
	return sizeof(*user);
}

uint32_t User_Seek(uint32_t offset, uint8_t flag){
	BD_Lock();
	uint32_t cursor = RingChunk_Seek(&userChunk, offset, flag);
	BD_Unlock();
	return cursor;
}
#endif 


/*----------------------------------------------------------------------*/
static void User_IncCountFlag(RingChunk_T* chunk, uint32_t index, User_T* user){
	int c = BitBuffer_ConsecutiveZeroBits(user->useFlag, sizeof(user->useFlag));
	BitBuffer_write(user->useFlag, c, 0);
	RingChunk_Write(chunk, index, user, sizeof(*user));
}

static int User_FlagCount(User_T* user){
	return BitBuffer_ConsecutiveZeroBits(user->useFlag, sizeof(user->useFlag));
}

uint8_t User_Used(User_T* user){
	return user->used +User_FlagCount(user);
}

static void User_UpdateCount(RingChunk_T* chunk, uint32_t index, User_T* user){
	user->used = User_Used(user) + 1;
	memset(user->useFlag, 0xFF, sizeof(user->useFlag));
	Chunk_DoModify(chunk, index, user);
}

void User_IncreaseCount(RingChunk_T* chunk, uint32_t index, User_T* user){
	if(User_FlagCount(user) == (sizeof(user->useFlag) * 8)){
		User_UpdateCount(chunk, index, user);
	}
	else{
		User_IncCountFlag(chunk, index, user);
	}
}


int User_DoIncCount(RingChunk_T* chunk, Uid_T uid){
	User_T user;
	uint32_t index;
	int ret = RingChunk_FindItemNext(chunk, uid, &user, &index);
	if(ret){
		return 1;
	}
	if(User_Used(&user) >= user.count){
		return -1;
	}
	User_IncreaseCount(chunk, index, &user);
	return 0;
}

int User_IncCount(RingChunk_T* chunk, Uid_T uid){
	int ret;
	BD_Lock();
	ret = User_DoIncCount(chunk, uid);
	BD_Unlock();
	return ret;
}

void User_Init(void){
	Chunk_Init(&userChunk, BD_Sec_UserStart, BD_Sec_UserInfoNum,
		BD_Sec_UserData, BD_Sec_UserDataNum, sizeof(User_T), USER_MAX);
}

#if 0
static int User_DummyCheck(void){
	uint32_t footprint = RingChunk_Footprint(&userChunk);	
	uint32_t size = userChunk.size;
	
	if(footprint > userChunk.itemPerPage){
		if(size == 0){
			return 1;
		}
		if((footprint / size) >= 2){
			//冗余50%
			return 1;
		}
	}

	return 0;
}

static void User_DoClean(void){
	RingChunk_T oldChunk = userChunk;
	//游标指向新的空间并初始化
	RingChunk_Clear(&userChunk);
	RingChunk_Seek(&oldChunk, 0, 0);
	while(oldChunk.size > 0){
		//查找下一个有效用户
		if(RingChunk_FindNext(&oldChunk, 0, 0)){
			break;
		}
		User_T user;
		RingChunk_CachReadItem(&oldChunk, &user);
		if(RingChunk_Append(&userChunk, &user)){
			break;
		}
		oldChunk.size--;
	}
}

void User_Clean(void){
	BD_Lock();
	if(RingChunk_Footprint(&userChunk) > userChunk.size){
		User_DoClean();
	}
	if(userChunk.changed){
		RingChunk_UpdateInfo(&userChunk);
	}
	BD_Unlock();
}

/*
用户信息管理服务函数
*/
void User_Service(void){
	//用户占用空间过多?
	if(User_DummyCheck()){
		//清理用户信息
		User_Clean();
	}
}

static void DebugUserChunk(RingChunk_T* chunk){
	MDBG(USER_DBG, "\n%s\n", "UserChunk");
	MDBG(USER_DBG, "chunk->baseSector=%d\n", chunk->baseSector);
	MDBG(USER_DBG, "chunk->itemCapacity=%d\n", chunk->itemCapacity);
	MDBG(USER_DBG, "chunk->itemPerPage=%d\n", chunk->itemPerPage);
	MDBG(USER_DBG, "chunk->itemSize=%d\n", chunk->itemSize);
	MDBG(USER_DBG, "chunk->head=%d\n", chunk->head);
	MDBG(USER_DBG, "chunk->tail=%d\n", chunk->tail);	
	MDBG(USER_DBG, "\n");
}

static void User_RemoveDummy(void){
	if(RingChunk_Footprint(&userChunk) < 2){
		return;
	}
	Uid_T uid;
	uint32_t i = userChunk.head - 1;
	//获取最近添加的uid
	RingChunk_Read(&userChunk, i, &uid, sizeof(uid));
	if(uid == 0 || uid == 0xFFFFFFFF){
		//uid非法，退出
		return;
	}
	//uid是否有其他旧的副本
	uint32_t index;
	if(RingChunk_FindItemNext(&userChunk, uid, 0, &index)){
		return;
	}
	if(index == i){
		return;
	}
	//删除旧uid
	uid = 0u;
	RingChunk_Write(&userChunk, index, &uid, sizeof(uid));
}


/*
初始化用户信息记录
*/
void User_Init(void){
	BD_Lock();
	RingChunk_Setup(&userChunk, BD_Sec_UserStart, BD_Sec_UserInfoNum,
		BD_Sec_UserData, BD_Sec_UserDataNum, sizeof(User_T), USER_MAX);
	/*
	在调用User_Set时出现掉电，可能导致冗余的用户信息，在此我们清理此冗余信息。
	*/
	User_RemoveDummy();
	BD_Unlock();
	DebugUserChunk(&userChunk);
}
#endif 


